package io.kimmking.common.redis.distributeLock;

import io.kimmking.dubbo.common.unit.ThrowableActionV2;
import io.kimmking.dubbo.common.distributelock.DistributedLock;
import io.kimmking.dubbo.common.distributelock.ZLock;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;

import java.util.concurrent.TimeUnit;

@Slf4j
@ConditionalOnClass(RedissonClient.class)
@ConditionalOnProperty(prefix = "kimmking.lock", name = "lockerType", matchIfMissing = true, havingValue = "REDIS")
public class RedissonDistributeLock implements DistributedLock{
    private static final String lock_key_prefix = "LOCK_KEY";

    //已自动注入，see org.redisson.spring.starter.RedissonAutoConfiguration
    @Autowired
    private RedissonClient redissonClient;

    private ZLock getLock(String key, boolean isFair) {
        RLock lock;
        if (isFair) {
            lock = redissonClient.getFairLock(lock_key_prefix + ":" + key);
        } else {
            lock = redissonClient.getLock(lock_key_prefix + ":" + key);
        }
        return new ZLock(lock, this);
    }

    @Override
    public ZLock lock(String key, long leaseTime, TimeUnit unit, boolean isFair) throws Exception {
        ZLock zLock = getLock(key, isFair);
        RLock lock = (RLock)zLock.getLock();
        lock.lock(leaseTime, unit);
        return zLock;
    }

    @Override
    public ZLock tryLock(String key, long waitTime, long leaseTime, TimeUnit unit, boolean isFair) throws Exception {
        ZLock zLock = getLock(key, isFair);
        RLock lock = (RLock) zLock.getLock();
        if (lock.tryLock(waitTime, leaseTime, unit)) {
            return zLock;
        }
        return null;
    }

    @Override
    public void unlock(Object lock){
        ThrowableActionV2.execute(() -> {
            RLock rLock = (RLock) lock;
            if (rLock.isLocked()) {
                rLock.unlock();
            }
        });
    }
}
